#pragma once
#include <base_definition.h>
#include <config.h>

#define LIMIT_MAX 0x000fffff
#define BIT(n) (1 << (n))
#define SETBIT(a, b) ((a) & (b) == (b))

/**
 * Limit    20bits  0x000f_ffff
 * Base     32bits  0xffff_ffff
 */

/**
 * F E D C B A 9 8 7 6 5 4 3 2 1 0
 *  |D| |A|       | |D  | | |E|W|A
 * G|/|L|V|       |P| P |S|X| | |
 *  |B| |L|       | |  L| | |C|R|A
 */

/**
 * G:       段颗粒大小 G=0 -> 1B; G=1 -> 4KB
 * D/B:     段的位数，影响默认操作数长度、栈指针大小、数据段边界
 *      D/B=0 -> 16bit; D/B=1 -> 32bit;
 * L:       64位段    L=1 -> 64bit & D==0; L=0 -> 32/16bit
 * AVL:     保留供系统使用
 */
#define SD_G BIT(15)
#define SD_D BIT(14)
#define SD_B BIT(14)
#define SD_L BIT(13)
#define SD_AVL BIT(12)

/**
 * P:       段存在性    P=0 -> 不存在，访问产生错误; P=1 -> 存在
 * S:       种类  S=0 -> 系统段; S=1 -> 代码段/数据段
 * X:       可执行   X=0 -> 数据段; X=1 -> 代码段
 */
#define SD_P BIT(7)
#define SD_DPL3 (3 << 5)
#define SD_DPL2 (2 << 5)
#define SD_DPL1 (1 << 5)
#define SD_DPL0 (0 << 5)
#define SD_S BIT(4)

/**
 * E/W/A:   向下扩展/可写/已访问
 * C/R/A:   一致/可读/已访问
 */
#define SD_X BIT(3)
#define SD_E BIT(2)
#define SD_C BIT(2)
#define SD_W BIT(1)
#define SD_R BIT(1)
#define SD_A BIT(0)

typedef struct packed
{
    u16 limit1;
    u16 base1;
    u8 base2;
    u16 attr;
    u8 base3;
} GDTEntry;

typedef struct packed
{
    u16 limit;
    u32 base;
} GdtPtr;

typedef u16 Selector;

typedef struct packed
{
    u64 Base;   // base address
    u64 Length; // length
    u32 Type;   // entry Type
    u32 ACPI;   // extended
} SMAPEntry;

static inline void InitGdtEntry(GDTEntry *sd, u16 attr);
static inline Selector GetSelector(u8 index);

void InitGdtEntry(GDTEntry *sd, u16 attr)
{
    u32 limit = LIMIT_MAX;
    u32 base = 0;
    sd->limit1 = limit & 0x0000ffff;   // 15..0  (16bits)
    sd->base1 = base & 0x0000ffff;     // 15..0  (16bits)
    sd->base2 = (base >> 16) & 0x00ff; // 23..16 (8bits)
    sd->attr = ((limit >> 8) & 0x0f00) // 19..16 (4bits)
               | (attr & 0xf0ff);
    sd->base3 = (base >> 24) & 0xff; // 31..24 (8bits)
}

Selector GetSelector(u8 index)
{
    return index << 3;
}
